﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using System.Xml;
using Ionic.Zip;
using System.IO;
using System.IO.Compression;
using MacomberMapSystem.Common.Database;
using MacomberMapSystem.Common.Serialization;

namespace MacomberMapSystem.Common.Processing
{
    /// <summary>
    /// This class is responsible for restoring a database
    /// </summary>
    public class MM_Restore_Database : CIM_Processor
    {
        #region Variable declarations
        /// <summary>The path to our input file</summary>
        private String FilePath;
        #endregion

        #region Initialization
        /// <summary>
        /// Initiate a new backup database process
        /// </summary>
        /// <param name="xElem"></param>
        /// <param name="NamedParameters"></param>
        /// <param name="Repository"></param>
        public MM_Restore_Database(XmlElement xElem, Dictionary<String, Object> NamedParameters, MM_Repository Repository)
            : base(xElem, NamedParameters, Repository)
        { }

        /// <summary>
        /// Report the name of our process
        /// </summary>
        public override string ProcessName
        {
            get { return "Restore database"; }
        }

        /// <summary>
        /// Execute our datbase backup
        /// </summary>
        /// <param name="FileName"></param>
        /// <param name="DbConn"></param>
        public void InitiateProcess(String FileName, MM_Database_Connector DbConn)
        {
            FilePath = FileName;
            InitiateProcessInPooledThread(DbConn);
        }

        /// <summary>
        /// Execute our database backup
        /// </summary>
        /// <param name="DbConn"></param>
        public override enumProcessState InitiateProcess(MM_Database_Connector DbConn)
        {
            XmlDocument xDocToRead = new XmlDocument();

            //First, open up our file
            if (FilePath.EndsWith(".zip", StringComparison.CurrentCultureIgnoreCase))
                using (ZipInputStream zS = new ZipInputStream(FilePath))
                {
                    zS.GetNextEntry();
                    xDocToRead.Load(zS);
                }
            else if (FilePath.EndsWith(".gz"))
                using (FileStream fS = new FileStream(FilePath, FileMode.Open, FileAccess.Read))
                using (GZipStream gS = new GZipStream(fS, CompressionMode.Decompress))
                    xDocToRead.Load(gS);
            else if (FilePath.EndsWith(".xml"))
                using (FileStream fS = new FileStream(FilePath, FileMode.Open, FileAccess.Read))
                    xDocToRead.Load(fS);

            //Build our list of totals for processing
            this.Total = 0;
            foreach (XmlElement xElem in xDocToRead.DocumentElement.ChildNodes)
                this.Total += 1 + xElem.ChildNodes.Count;


            //Now, go through table by table, and process
            foreach (XmlElement xElem in xDocToRead.DocumentElement.ChildNodes)
                if (xElem.Name == "Sequence")
                    ReadSequence(xElem);
                else
                    ReadTable(xElem);
            return enumProcessState.Completed;
        }
        #endregion

        #region Exporting
        /// <summary>
        /// Read in a sequence
        /// </summary>
        /// <param name="xElem"></param>
        private void ReadSequence(XmlElement xElem)
        {
            //First, clear the sequence if it exists
            using (DbCommand dCmd = Repository.Db.CreateCommand("DELETE FROM USER_SEQUENCES WHERE SEQUENCE_NAME=" + xElem.Attributes["SEQUENCE_NAME"].Value))
                dCmd.ExecuteNonQuery();

            //Now, add in the parameters
            StringBuilder sB = new StringBuilder();
            StringBuilder sB2 = new StringBuilder();
            foreach (XmlAttribute xAttr in xElem.Attributes)
                if (sB.Length == 0)
                {
                    sB.Append("INSERT INTO USER_SEQUENCES (\"" + xAttr.Name + "\"");
                    sB2.Append(") VALUES ('" + xAttr.Value + "'");
                }
                else
                {
                    sB.Append(", \"" + xAttr.Name + "\"");
                    sB2.Append(") VALUES ('" + xAttr.Value + "'");
                }

            using (DbCommand dCmd = Repository.Db.CreateCommand(sB.ToString() + sB2.ToString() + ")"))
                dCmd.ExecuteNonQuery();
            Current++;
        }

        /// <summary>
        /// Drop, then re-write a table
        /// </summary>
        /// <param name="xElem"></param>
        private void ReadTable(XmlElement xElem)
        {
            //First, pull in our definition of columns
            Dictionary<String, String> Columns = new Dictionary<string, string>();
            foreach (XmlElement xCol in xElem["Columns"].ChildNodes)
                Columns.Add(xCol.Attributes["Name"].Value, xCol.Attributes["Type"].Value.Split('(')[0]);

            //Now, drop our table and re-run the DDL.
            using (DbTransaction dTrans = Repository.Db.BeginTransaction())
            {
                using (DbCommand dCmd = Repository.Db.CreateCommand(dTrans, "DROP TABLE " + xElem.Attributes["TABLE_NAME"].Value))
                    dCmd.ExecuteNonQuery();
                using (DbCommand dCmd = Repository.Db.CreateCommand(dTrans, xElem["DDL"].InnerText))
                    dCmd.ExecuteNonQuery();
                Current++;

                //Now, Write in line by line
                foreach (XmlElement xChild in xElem.ChildNodes)
                    if (xChild.Name == "Row")
                    {
                        Dictionary<String, object> OutVals = new Dictionary<string, object>();
                        foreach (XmlAttribute xAttr in xChild.Attributes)
                            if (Columns[xAttr.Name] == "VARCHAR2")
                                OutVals.Add(xAttr.Name, xAttr.Value);
                            else if (Columns[xAttr.Name] == "NUMBER")
                                OutVals.Add(xAttr.Name, Convert.ToDouble(xAttr.Value));
                            else if (Columns[xAttr.Name] == "DATE" || Columns[xAttr.Name] == "TIMESTAMP")
                                OutVals.Add(xAttr.Name, XmlConvert.ToDateTime(xAttr.Value, XmlDateTimeSerializationMode.Unspecified));
                            else if (Columns[xAttr.Name] == "CLOB")
                                OutVals.Add(xAttr.Name, new UTF8Encoding().GetString(Convert.FromBase64String(xAttr.Value)));
                            else if (Columns[xAttr.Name] == "BLOB")
                                OutVals.Add(xAttr.Name, Convert.FromBase64String(xAttr.Value));
                        Repository.Db.InsertRow(dTrans, xElem.Attributes["TABLE_NAME"].Value, OutVals);
                        Current++;
                    }
                dTrans.Commit();
            }
        }
        #endregion

    }
}